/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package TimeRecording.TimeClockServer;

import TimeRecording.TimeClockServer.messages.ClockMessage;
import TimeRecording.TimeClockServer.enums.TxOrRx;
import TimeRecording.TimeClockServer.dtos.TxRxBytes;
import TimeRecording.TimeClockServer.messages.tx.SetTimeMessage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * This class is responsible for transmitting messages to the clocking terminal.
 * It has a single incoming interface (a queue belonging to the parent class
 * that must be periodically polled) and a single outgoing interface (a TCP/IP
 * socket connected to the clock terminal).
 *
 * The class will typically pull a message from the queue and transmit it to the
 * terminal before waiting for an acknowledgement.
 *
 * @author dpatchett
 */
public class ClockSender extends Thread {

    // The amount time that this thread will wait before retrying
    // or abandoning the transmission of a specific message
    private final static int ACK_TIMEOUT = 100;

    // The number of times we'll wait for an acknowledgement
    private final static int ACK_RETRIES = 50;

    // The amount of time between "polls" of the transmission buffer.
    // This "pause" is only used when the transmission buffer is empty.
    private final static int IDLE_TIMEOUT = 100;

    // The parent that coordinates the whol send/receive process
    private ClockServer parent;

    private OutputStream outStream;

    // Constructs a new ClockSender object with details of its parent
    public ClockSender(ClockServer parent, OutputStream outStream) {
        this.parent = parent;
        this.outStream = outStream;
    }

    /**
     * This method (a required component of any thread) is responsible for doing
     * all of the work of the sender.  Unless interrupted, it will repeatedly
     * poll the [thread safe] transmission buffer.  If something is ready to be
     * sent then a message will be "popped" from the queue and transmitted.
     *
     * Most messages require a positive acknowledgement; for such messages, this
     * thread will wait for such before proceeding to send the next message (if
     * there is one).  The acknowledgement is received via the receive thread
     * and communicated to this thread via the receive buffer.
     *
     * Priority is given to acknowledgement of received messages.  Thus, we
     * always check for this condition before polling the actual queue.
     *
     * Note also the time updates are not just queued; if they were then we
     * might end up sending a stale time.
     */
    @Override
    @SuppressWarnings("SleepWhileHoldingLock")
    public void run() {

         try {
            do {
                ClockMessage message = null;

                // Do we need to send an acknowledgement?
                if (parent.getTxBuffer().testAckRequired()) {
                    message = ClockMessage.ACK;

                // Do we need to send the time
                } else if (parent.getTxBuffer().testTimeToBeTransmitted()) {                    
                    // We'll send the message shortly
                    message = new SetTimeMessage(new Date());
                    // Queue a poll to check the clock has re-synched
                    parent.getTxBuffer().addMessage(ClockMessage.REQUEST_TIME);
                    
                // Or do we need to send a message?
                } else if (!parent.getTxBuffer().isEmpty()) {
                    message = parent.getTxBuffer().removeMessage();

                // Or do we take a short nap before checking again?
                } else {
                    Thread.sleep(IDLE_TIMEOUT);
                    continue;
                }

                // Log the message with the controlling thread
                parent.deferredLogTxRxBytes(new TxRxBytes(message.getBytes(),
                        message.getBytes().length, null, TxOrRx.TX));

                // Send the message to the clock terminal (appending
                // an EOT if that's how we're operating)
                outStream.write(message.getBytes());
                if (ClockServer.USING_EOT_MARKER) {
                    outStream.write(ClockMessage.EOT);
                }

                // If we expect an acknowledgement then we ought to wait for it
                if (message.ackRequired()) {
                    waitForAcknowledgement();
                }

            } while (!interrupted());

        } catch (InterruptedException ex) {
            Logger.getLogger(ClockSender.class.getName()).log(Level.SEVERE, null, ex);

        } catch (IOException ex) {
            Logger.getLogger(ClockSender.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * This method sill simply poll the transmission buffer to see if an
     * acknowledgement has been recorded.  If there is no acknowledgement then
     * the thread will pause for a short delay then retry; this pause/retry
     * sequence will be repeated a number of times before the operation is
     * abandoned;
     *
     * @return a boolean indicating receipt or otherwise of the acknowledgement
     * @throws InterruptedException
     */
    @SuppressWarnings("SleepWhileHoldingLock")
    private boolean waitForAcknowledgement() throws InterruptedException {

        int ackRetries = ACK_RETRIES;

        do {
            if (parent.getRxBuffer().testAckReceived()) {
                return true;
            } else {
                Thread.sleep(ACK_TIMEOUT);
            }
        } while (--ackRetries > 0);

        return false;
    }
}